<?php

/**
 * @see Zend_Http_Client
 */
require_once 'Zend/Http/Client.php';

/**
 * Consulta as informacoes geograficas de um endereco de IP valido. 
 * 
 * @category Tavs
 * @package Tavs_GeoIp
 *
 * @author Tales Santos <tales.augusto.santos>
 */
class Tavs_GeoIp
{
	/**
	 * Informacoes pesquisadas com sucesso
	 *
	 */
	const CODE_OK = 'OK';
	
	/**
	 * IP a ser pesquisado
	 *
	 * @var string
	 */
	protected $_ipAddress;
	
	/**
	 * URL a ser consultada as informacoes
	 *
	 * @var string
	 */
	protected $_url = 'http://ipinfodb.com/ip_query.php?ip=%s';
	
	/**
	 * Resultado da consulta
	 *
	 * @var stdClass
	 */
	protected $_queryResult;
	
	/**
	 * Atributos a serem renomeados
	 *
	 * @var array
	 */
	protected $_resultMap = array(
		'City' 		=> 'cityName',
		'Gmtoffset' => 'gmtOffset',
		'Dstoffset' => 'dstOffset',
	);
	
	/**
	 * Construtor
	 *
	 * @param string $ipAddress IP a ser consultado
	 */
	public function __construct($ipAddress)
	{
		$this->_setIpAddress($ipAddress);
	}

	/**
	 * Seta o endereco IP a ser consultado
	 *
	 * @param string $ipAddress IP a ser consultado
	 * 
	 * @return Tavs_GeoIp
	 */
	public function _setIpAddress($ipAddress)
	{
		$ipAddress = trim($ipAddress);

		if ($this->_checkIp($ipAddress)) {
			$this->_ipAddress = $ipAddress;
		}
		else {
			throw new Tavs_GeoIp_Exception('invalid sintax for ' . $ipAddress);
		}
		
		return $this;
	}

	/**
	 * Checa se ja foi feita a consulta ao site
	 *
	 * @return bool
	 */
	protected function _hasQueried()
	{
		return ($this->_queryResult !== null);
	}

	/**
	 * Checa se o endereco IP e valido
	 *
	 * @param string $ipAddress Endereco IP a ser checado
	 * @return bool
	 */
	public function _checkIp($ipAddress)
	{
		$pattern = '/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/';
		return (preg_match($pattern, $ipAddress) > 0);
	}

	/**
	 * Retorna a URL que fara a consulta
	 *
	 * @return string
	 */
	public function _getUrl()
	{
		if (null !== $this->_ipAddress) {
			$url = sprintf($this->_url, $this->_ipAddress);
			return $url;
		}
		else {
			throw new Tavs_GeoIp_Exception('ip address must be setted before get url');
		}
	}

	/**
	 * Retorna o resultado da consulta
	 *
	 * @return stdClass
	 */
	public function query()
	{
		if (!$this->_hasQueried()) {
			$this->_query();
		}
		
		return $this->_queryResult;
	}

	/**
	 * Faz a consulta ao endereco IP
	 *
	 * @return false|stdClas Resultado da consulta
	 */
	protected function _query()
	{
		$url = $this->_getUrl();
		$request = new Zend_Http_Client($url);

		$response = $request->request(Zend_Http_Client::GET);

		//requisicao OK
		if ($response->getStatus() == 200) {
			
			$xml = $response->getBody();
			$queryResult = $this->_queryResult = $this->_parseXml($xml);
			
			if ($this->status != self::CODE_OK) {
				throw new Tavs_GeoIp_Exception('server was returned message ' . $this->status);
			}

		}
		else {
			throw new Tavs_GeoIp_Exception('error requesting the ip information: ' . $response->getMessage());
		}
	}

	/**
	 * Le o XML retornado na requisicao
	 *
	 * @param string $xml XML a ser parseado
	 * 
	 * @return stdClass Resultado da consulta
	 */
	protected function _parseXml($xml)
	{
		try {

			$xml = new SimpleXMLElement($xml);
			$result = new stdClass();

			foreach ($xml->children() as $attrib => $child) {

				if (array_key_exists($attrib, $this->_resultMap)) {
					$attrib = $this->_resultMap[$attrib];
				}

				$value = current($child);
				if (false === $value) {
					$value = '';
				}

				$attrib = $this->_formatProperty($attrib);
				$result->{$attrib} = $value;
			}

			return $result;
		}
		catch (Exception $e) {
			throw new Tavs_GeoIp_Exception('error parsing xml: "' . $e->getMessage() . '"');
		}
	}

	/**
	 * Formata os atributos a serem inseridos em $_queryResult
	 *
	 * @param string $property Propriedade a ser formatada
	 * 
	 * @return string
	 */
	protected function _formatProperty($property)
	{
		$property = (string)$property;
		$property = strtolower($property[0]) . substr($property, 1);
		return $property;
	}

	/**
	 * Retorna uma informacao do resultado da pesquisa
	 *
	 * @param string $name Chave da informacao a ser retornada
	 * 
	 * @return string
	 */
	public function __get($name)
	{
		$result = $this->query();
		
		if (property_exists($result, (string)$name)) {
			return $result->{$name};
		}
		else {
			throw new Tavs_GeoIp_Exception('property ' . $name . ' not exists in result');
		}
	}

	/**
	 * Converte o resultado para array
	 *
	 * @return array
	 */
	public function toArray()
	{
		$result = $this->query();

		$array = array();
		foreach ($result as $key => $value) {
			$array[$key] = $value;
		}

		return $array;
	}
}